home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 2.toast / pc / sample code / quicktime / quicktimeintro / wiredsprites / common files / spriteutilities.c < prev    next >
Encoding:
Text File  |  2000-10-06  |  17.4 KB  |  499 lines

  1. //////////
  2. //
  3. //    File:        SpriteUtilities.c
  4. //
  5. //    Contains:    Utilities for adding sprite tracks to QuickTime movies.
  6. //
  7. //    Written by:    Sean Allen
  8. //    Revised by:    Chris Flick and Tim Monroe
  9. //
  10. //    Copyright:    © 1997-1998 by Apple Computer, Inc., all rights reserved.
  11. //
  12. //    Change History (most recent first):
  13. //
  14. //       <3>         03/27/98    rtm        added error checking to AddPICTImageToKeyFrameSample to prevent crashes
  15. //                                    if PICT resources not found
  16. //       <2>         03/27/98    cf        further fixes for Windows compiles
  17. //       <1>         03/26/98    rtm        made fixes for Windows compiles
  18. //       
  19. //
  20. //////////
  21.  
  22. #ifndef _SPRITEUTILITIES_
  23. #include "SpriteUtilities.h"
  24. #endif
  25.  
  26. #ifndef _IMAGECOMPRESSIONUTILITIES_
  27. #include "ImageCompressionUtilities.h"
  28. #endif
  29.  
  30. #ifndef __ENDIANUTILITIES__
  31. #include "EndianUtilities.h"
  32. #endif
  33.  
  34. #ifndef __RESOURCES__
  35. #include <Resources.h>
  36. #endif
  37.  
  38. #ifndef __QUICKTIMECOMPONENTS__
  39. #include <QuickTimeComponents.h>
  40. #endif
  41.  
  42. #ifndef __MEDIAHANDLERS__
  43. #include <MediaHandlers.h>
  44. #endif
  45.  
  46. // exception handling macros
  47.  
  48. #define        FailIf(a, e)         {if (a)     { err = e; goto bail; }}
  49. #define        FailOSErr(a)         {if (err = a)     goto bail;}
  50. #define        FailMemErr(a)        {a; if (err = MemError()) goto bail;}
  51.  
  52. OSErr GetImageDescription( QTAtomContainer keySample, QTAtom imagesContainerAtom, short imageIndex, ImageDescriptionHandle imageDesc );
  53. OSErr SetImageGroupID( QTAtomContainer keySample, QTAtom imagesContainerAtom, short imageIndex, long groupID );
  54. OSErr GetImageGroupID( QTAtomContainer keySample, QTAtom imagesContainerAtom, short imageIndex, long *groupID );
  55.  
  56. OSErr SetSpriteData( QTAtomContainer sprite, Point *location, short *visible, short *layer, short *imageIndex, ModifierTrackGraphicsModeRecord *graphicsMode, StringPtr spriteName, QTAtomContainer actionAtoms )
  57. {
  58.     OSErr    err = noErr;
  59.     QTAtom    propertyAtom;
  60.     
  61.     if ( location ) {
  62.         MatrixRecord    matrix;
  63.         
  64.         SetIdentityMatrix( &matrix );
  65.         matrix.matrix[2][0] = ((long)location->h << 16);
  66.         matrix.matrix[2][1] = ((long)location->v << 16);
  67.         
  68.         EndianUtils_MatrixRecord_NtoB( &matrix );
  69.  
  70.         if ( (propertyAtom = QTFindChildByIndex( sprite, 0, kSpritePropertyMatrix, 1, nil )) == 0 )
  71.             FailOSErr( QTInsertChild( sprite, 0, kSpritePropertyMatrix, 1, 0, sizeof(MatrixRecord), &matrix, nil ) )
  72.         else
  73.             FailOSErr( QTSetAtomData( sprite, propertyAtom, sizeof(MatrixRecord), &matrix ) );
  74.     }
  75.     if ( visible ) {
  76.         short tempVisible = *visible;
  77.         
  78.         tempVisible = EndianS16_NtoB(tempVisible);
  79.         
  80.         if ( (propertyAtom = QTFindChildByIndex( sprite, 0, kSpritePropertyVisible, 1, nil )) == 0 )
  81.             FailOSErr( QTInsertChild( sprite, 0, kSpritePropertyVisible, 1, 0, sizeof(short), &tempVisible, nil ) )
  82.         else
  83.             FailOSErr( QTSetAtomData( sprite, propertyAtom, sizeof(short), &tempVisible ) );
  84.     }
  85.     if ( layer ) {
  86.         short tempLayer = *layer;
  87.         
  88.         tempLayer = EndianS16_NtoB(tempLayer);
  89.  
  90.         if ( (propertyAtom = QTFindChildByIndex( sprite, 0, kSpritePropertyLayer, 1, nil )) == 0 )
  91.             FailOSErr( QTInsertChild( sprite, 0, kSpritePropertyLayer, 1, 0, sizeof(short), &tempLayer, nil ) )
  92.         else
  93.             FailOSErr( QTSetAtomData( sprite, propertyAtom, sizeof(short), &tempLayer ) );
  94.     }
  95.     if ( imageIndex ) {
  96.         short tempImageIndex = *imageIndex;
  97.  
  98.         tempImageIndex = EndianS16_NtoB(tempImageIndex);
  99.         
  100.         if ( (propertyAtom = QTFindChildByIndex( sprite, 0, kSpritePropertyImageIndex, 1, nil )) == 0 )
  101.             FailOSErr( QTInsertChild( sprite, 0, kSpritePropertyImageIndex, 1, 0, sizeof(short), &tempImageIndex, nil ) )
  102.         else
  103.             FailOSErr( QTSetAtomData( sprite, propertyAtom, sizeof(short), &tempImageIndex ) );
  104.     }
  105.     if ( graphicsMode ) {
  106.         ModifierTrackGraphicsModeRecord    tempGraphicsMode;
  107.         
  108.         tempGraphicsMode.graphicsMode    = EndianU32_NtoB(graphicsMode->graphicsMode);
  109.         tempGraphicsMode.opColor.red     = EndianU16_NtoB(graphicsMode->opColor.red);
  110.         tempGraphicsMode.opColor.green    = EndianU16_NtoB(graphicsMode->opColor.green);
  111.         tempGraphicsMode.opColor.blue    = EndianU16_NtoB(graphicsMode->opColor.blue);
  112.  
  113.         if ( (propertyAtom = QTFindChildByIndex( sprite, 0, kSpritePropertyGraphicsMode, 1, nil )) == 0 )
  114.             FailOSErr( QTInsertChild( sprite, 0, kSpritePropertyGraphicsMode, 1, 0, sizeof(tempGraphicsMode), &tempGraphicsMode, nil ) )
  115.         else
  116.             FailOSErr( QTSetAtomData( sprite, propertyAtom, sizeof(tempGraphicsMode), &tempGraphicsMode ) );
  117.     }
  118.     if( spriteName ) {
  119.         QTAtom spriteNameAtom;
  120.         
  121.         if ( (spriteNameAtom = QTFindChildByIndex( sprite, 0, kSpriteNameAtomType, 1, nil )) == 0 )
  122.             FailOSErr( QTInsertChild( sprite, 0, kSpriteNameAtomType, 1, 0, spriteName[0]+1, spriteName, nil ) )
  123.         else
  124.             FailOSErr( QTSetAtomData( sprite, spriteNameAtom, spriteName[0]+1, spriteName ) );
  125.     }
  126.     
  127.     if ( actionAtoms ) {
  128.         FailOSErr( QTInsertChildren( sprite, kParentAtomIsContainer, actionAtoms ) );
  129.     }
  130.     
  131. bail:
  132.     if ( err && sprite )
  133.         QTRemoveChildren( sprite, 0 );
  134.  
  135.     return err;
  136. }
  137.  
  138. OSErr AddSpriteToSample( QTAtomContainer theSample, QTAtomContainer theSprite, QTAtomID spriteID )
  139. {
  140.     OSErr    err = noErr;
  141.     QTAtom    newSpriteAtom;
  142.     
  143.     FailIf ( QTFindChildByID( theSample, 0, kSpriteAtomType, spriteID, nil ), paramErr );
  144.     
  145.     FailOSErr( QTInsertChild( theSample, 0, kSpriteAtomType, spriteID, 0, 0, nil, &newSpriteAtom ) );        // index of zero means append
  146.     FailOSErr( QTInsertChildren( theSample, newSpriteAtom, theSprite ) );
  147. bail:
  148.     return err;
  149. }
  150.  
  151. OSErr AddSpriteSampleToMedia( Media theMedia, QTAtomContainer sample, TimeValue duration, Boolean isKeyFrame,
  152.                         TimeValue *sampleTime )
  153. {
  154.     OSErr                    err = noErr;
  155.     SampleDescriptionHandle sampleDesc = nil;
  156.     
  157.     FailMemErr( sampleDesc = (SampleDescriptionHandle) NewHandleClear( sizeof(SpriteDescription ) ) );
  158.     
  159.     FailOSErr( AddMediaSample(     theMedia, (Handle) sample, 0, GetHandleSize( sample ),
  160.                                 duration, sampleDesc, 1,
  161.                                 (short)(isKeyFrame ? 0 : mediaSampleNotSync), sampleTime ) );
  162. bail:
  163.     if ( sampleDesc )    DisposeHandle( (Handle) sampleDesc );
  164.     return err;
  165. }
  166.  
  167.  
  168. OSErr AddCompressedSpriteSampleToMedia( Media theMedia, QTAtomContainer sample, TimeValue duration, Boolean isKeyFrame,
  169.                         OSType dataCompressorType,
  170.                         TimeValue *sampleTime )
  171. {
  172.     OSErr                    err = noErr;
  173.     SpriteDescriptionHandle sampleDesc = nil;
  174.     Handle                    compressedSample = nil;
  175.     ComponentInstance        dataCompressorComponentInstance = nil;
  176.     
  177.     err = OpenADefaultComponent(DataCompressorComponentType, dataCompressorType, &dataCompressorComponentInstance);
  178.     if( err ) goto bail;
  179.  
  180.     FailMemErr( sampleDesc = (SpriteDescriptionHandle) NewHandleClear( sizeof(SpriteDescription) ) );
  181.     
  182.     if( dataCompressorComponentInstance != nil ) {
  183.         UInt32            compressBufferSize, actualCompressedSize, decompressSlop = 0;
  184.         UInt32            uncompressedSize;
  185.         SignedByte         saveState = HGetState( sample );
  186.         
  187.         err = (OSErr)DataCodecGetCompressBufferSize( dataCompressorComponentInstance, GetHandleSize( sample ), &compressBufferSize );
  188.         if(err) goto bail;
  189.         
  190.         compressedSample = NewHandle( sizeof(UInt32) + compressBufferSize );
  191.         err = MemError();
  192.         if(err) goto bail;
  193.         
  194.         HLockHi( sample );
  195.         HLockHi( compressedSample );
  196.         err = (OSErr)DataCodecCompress( dataCompressorComponentInstance, *sample, 
  197.                                 GetHandleSize(sample), 
  198.                                 *compressedSample + sizeof(UInt32),         // room for size at beginning
  199.                                 compressBufferSize, 
  200.                                 &actualCompressedSize,
  201.                                 &decompressSlop );
  202.         
  203.         HSetState( sample, saveState );
  204.         HUnlock( compressedSample );
  205.         
  206.         if(err) goto bail;
  207.         
  208.         SetHandleSize( compressedSample, sizeof(UInt32) + actualCompressedSize );
  209.         err = MemError();
  210.         if(err) goto bail;
  211.  
  212.         (**sampleDesc).decompressorType = EndianU32_NtoB(dataCompressorType);
  213.     
  214.         uncompressedSize = GetHandleSize(sample);
  215.         (*(UInt32*) *compressedSample) = EndianU32_NtoB(uncompressedSize);        // add uncompressed size at beginning
  216.         
  217.         FailOSErr( AddMediaSample(     theMedia, (Handle) compressedSample, 0, GetHandleSize( compressedSample ),
  218.                                     duration, (SampleDescriptionHandle) sampleDesc, 1,
  219.                                     (short)(isKeyFrame ? 0 : mediaSampleNotSync), sampleTime ) );
  220.     }
  221.     else
  222.     {
  223.         FailOSErr( AddMediaSample(     theMedia, (Handle) sample, 0, GetHandleSize( sample ),
  224.                                     duration, (SampleDescriptionHandle) sampleDesc, 1,
  225.                                     (short)(isKeyFrame ? 0 : mediaSampleNotSync), sampleTime ) );
  226.     }
  227.     
  228. bail:
  229.     if ( compressedSample )                 DisposeHandle( compressedSample );
  230.     if ( sampleDesc )                        DisposeHandle( (Handle) sampleDesc );
  231.     if ( dataCompressorComponentInstance )    CloseComponent( dataCompressorComponentInstance );
  232.     
  233.     return err;
  234. }
  235.  
  236.  
  237. OSErr AddPICTImageToKeyFrameSample( QTAtomContainer keySample, short pictID, RGBColor *keyColor, QTAtomID id, FixedPoint *registrationPoint, StringPtr imageName )
  238. {
  239.     OSErr                    err = noErr;
  240.     PicHandle                picture;
  241.     Handle                    compressedPicture = NULL;
  242.     ImageDescriptionHandle    idh = NULL;
  243.     
  244.     // get picture from resource
  245.     picture = (PicHandle) GetPicture( pictID );
  246.     if (picture == NULL)
  247.         err = resNotFound;
  248.  
  249.     if(err) goto bail;
  250.     
  251.     DetachResource( (Handle)picture );
  252.     
  253.     // convert it to image data compressed by the animation compressor
  254.     err = RecompressPictureWithTransparency( picture, keyColor, nil, &idh, &compressedPicture );
  255.     if(err) goto bail;
  256.  
  257.     // add it to the keySample
  258.     HLock( compressedPicture );
  259.     err = AddCompressedImageToKeyFrameSample( keySample, idh, GetHandleSize( compressedPicture ), *compressedPicture, id, registrationPoint, imageName );
  260.     
  261. bail:
  262.     if ( picture )                KillPicture( picture );
  263.     if ( compressedPicture )    DisposeHandle( compressedPicture );    
  264.     if ( idh )                    DisposeHandle( (Handle)idh );    
  265.     return err;
  266. }
  267.  
  268.  
  269.  
  270.  
  271. OSErr AddCompressedImageToKeyFrameSample( QTAtomContainer keySample, 
  272.                 ImageDescriptionHandle idh, long dataSize, Ptr compressedDataPtr, 
  273.                 QTAtomID imageID, FixedPoint *registrationPoint, StringPtr imageName )
  274. {
  275.     OSErr        err = noErr;
  276.     Handle        imageData;
  277.     QTAtom        defaultsAtom, imagesContainerAtom, imageAtom;
  278.     ImageDescriptionHandle    bigEndianImageDescription = nil;
  279.  
  280. #if TARGET_RT_LITTLE_ENDIAN
  281.     bigEndianImageDescription = (ImageDescriptionHandle) NewHandle(GetHandleSize((Handle)idh));
  282.     BlockMoveData( *idh, *bigEndianImageDescription, GetHandleSize((Handle)idh));
  283.     EndianUtils_ImageDescription_NtoB(bigEndianImageDescription);
  284. #else
  285.     bigEndianImageDescription = idh;            // already is big endian
  286. #endif
  287.  
  288.     // append compressed picture data to imageDescription to obtain sprite image data
  289.     FailMemErr( imageData = NewHandle(0) );
  290.     FailMemErr( HandAndHand( (Handle)bigEndianImageDescription, imageData ) );    // imageData <= imageData + bigEndianImageDescription
  291.     FailMemErr( PtrAndHand( compressedDataPtr, imageData, dataSize ) );
  292.     
  293.     if ( (defaultsAtom = QTFindChildByIndex( keySample, 0, kSpriteSharedDataAtomType, 1, nil )) == 0 )
  294.         FailOSErr( QTInsertChild( keySample, 0, kSpriteSharedDataAtomType, 1, 0, 0, nil, &defaultsAtom ) );
  295.         
  296.     if ( (imagesContainerAtom = QTFindChildByIndex( keySample, defaultsAtom, kSpriteImagesContainerAtomType, 1, nil )) == 0 )
  297.         FailOSErr( QTInsertChild( keySample, defaultsAtom, kSpriteImagesContainerAtomType, 1, 0, 0, nil, &imagesContainerAtom ) );
  298.  
  299.     FailOSErr( QTInsertChild( keySample, imagesContainerAtom, kSpriteImageAtomType, imageID, 0, 0, nil, &imageAtom ) );
  300.  
  301.     HLock( imageData );
  302.     FailOSErr( QTInsertChild( keySample, imageAtom, kSpriteImageDataAtomType, 1, 0, GetHandleSize(imageData), *imageData, nil ) );
  303.     HUnlock( imageData );
  304.     
  305.     if ( registrationPoint ) {
  306.         FixedPoint tempRegistrationPoint;
  307.         
  308.         tempRegistrationPoint.x = EndianS32_NtoB(registrationPoint->x);
  309.         tempRegistrationPoint.y = EndianS32_NtoB(registrationPoint->y);
  310.         
  311.         FailOSErr( QTInsertChild( keySample, imageAtom, kSpriteImageRegistrationAtomType, 1, 0, sizeof(tempRegistrationPoint), &tempRegistrationPoint, nil ) );
  312.     }
  313.     else {
  314.         FixedPoint regPoint = { 0, 0 };
  315.         
  316.         // Flipping {0,0} doesn't change anything so we don't flip
  317.         
  318.         FailOSErr( QTInsertChild( keySample, imageAtom, kSpriteImageRegistrationAtomType, 1, 0, sizeof(regPoint), ®Point, nil ) );
  319.     }
  320.     
  321.     if( imageName ) {
  322.         FailOSErr( QTInsertChild( keySample, imageAtom, kSpriteImageNameAtomType, 1, 0, imageName[0], &imageName[1], nil ) );
  323.     }
  324.  
  325. bail:
  326. #if TARGET_RT_LITTLE_ENDIAN
  327.     if(bigEndianImageDescription)
  328.         DisposeHandle((Handle)bigEndianImageDescription);
  329. #else
  330.     // bigEndianImageDescription is still idh, so don't dispose of it
  331. #endif
  332.  
  333.     if ( imageData )    DisposeHandle( imageData );    
  334.     return err;
  335. }
  336.  
  337. OSErr AssignImageGroupIDsToKeyFrame( QTAtomContainer keySample )
  338. {
  339.     OSErr                    err = noErr;
  340.     QTAtom                    defaultsAtom, imagesContainerAtom;
  341.     ImageDescriptionHandle    firstImageDesc = nil, secondImageDesc = nil;
  342.     short                    firstIndex, secondIndex, numImages;
  343.     CodecType                firstImageType, secondImageType;
  344.     long                    groupID = 0, testID;
  345.     
  346.     defaultsAtom = QTFindChildByIndex( keySample, 0, kSpriteSharedDataAtomType, 1, nil );
  347.     if ( ! defaultsAtom )    goto bail;
  348.     
  349.     imagesContainerAtom = QTFindChildByIndex( keySample, defaultsAtom, kSpriteImagesContainerAtomType, 1, nil );
  350.     if ( ! imagesContainerAtom )    goto bail;
  351.     
  352.     firstImageDesc = (ImageDescriptionHandle)NewHandle(0);
  353.     if ( firstImageDesc == nil ) { err = memFullErr; goto bail; }
  354.  
  355.     secondImageDesc = (ImageDescriptionHandle)NewHandle(0);
  356.     if ( secondImageDesc == nil ) { err = memFullErr; goto bail; }
  357.  
  358.     numImages = QTCountChildrenOfType( keySample, imagesContainerAtom, kSpriteImageAtomType );
  359.     for ( firstIndex = 1; firstIndex <= numImages; firstIndex++ ) {
  360.         FailOSErr( SetImageGroupID( keySample, imagesContainerAtom, firstIndex, 0 ) );
  361.     }
  362.     for ( firstIndex = 1; firstIndex <= (numImages - 1); firstIndex++ ) {
  363.         FailOSErr( GetImageGroupID( keySample, imagesContainerAtom, firstIndex, &testID ) );
  364.         if ( testID == 0 ) {
  365.             groupID++;
  366.             FailOSErr( SetImageGroupID( keySample, imagesContainerAtom, firstIndex, groupID ) );
  367.             
  368.             FailOSErr( GetImageDescription( keySample, imagesContainerAtom, firstIndex, firstImageDesc ) );
  369.             firstImageType = (**firstImageDesc).cType;
  370.             
  371.             for ( secondIndex = (firstIndex + 1); secondIndex <= numImages; secondIndex++ ) {
  372.                 FailOSErr( GetImageGroupID( keySample, imagesContainerAtom, secondIndex, &testID ) );
  373.                 if ( testID == 0 ) {
  374.                     FailOSErr( GetImageDescription( keySample, imagesContainerAtom, secondIndex, secondImageDesc ) );
  375.                     secondImageType = (**secondImageDesc).cType;
  376.                 
  377.                     if ( firstImageType == secondImageType ) {
  378.                         ImageSequence    seqID;
  379.                         Boolean            equivalent;
  380.                         
  381.                         FailOSErr( DecompressSequenceBegin( &seqID, firstImageDesc, nil, nil, nil, nil, 
  382.                                     ditherCopy, (RgnHandle)nil,  0, codecNormalQuality, anyCodec ) );
  383.                                      
  384.                         CDSequenceEquivalentImageDescription( seqID, secondImageDesc, &equivalent );
  385.  
  386.                         CDSequenceEnd( seqID );
  387.                         
  388.                         if ( equivalent ) {
  389.                             FailOSErr( SetImageGroupID( keySample, imagesContainerAtom, secondIndex, groupID ) );
  390.                         }
  391.                     }
  392.                 }
  393.             }
  394.         }
  395.     }
  396.     // assign an ID to the last image
  397.     FailOSErr( GetImageGroupID( keySample, imagesContainerAtom, numImages, &testID ) );
  398.     if ( testID == 0 ) {
  399.         groupID++;
  400.         FailOSErr( SetImageGroupID( keySample, imagesContainerAtom, numImages, groupID ) );
  401.     }
  402.     
  403. bail:
  404.     if ( firstImageDesc )    DisposeHandle( (Handle)firstImageDesc );
  405.     if ( secondImageDesc )    DisposeHandle( (Handle)secondImageDesc );
  406.     return err;
  407. }
  408.  
  409.  
  410.  
  411. // •••• ImageDescriptionHandles are in native endianism, right? If so, we need to endian flip them before
  412. //        they're returned from GetImageDescription.
  413.  
  414. OSErr GetImageDescription( QTAtomContainer keySample, QTAtom imagesContainerAtom, short imageIndex, ImageDescriptionHandle imageDesc )
  415. {
  416.     OSErr    err = noErr;
  417.     QTAtom    imageAtom, imageDataAtom;
  418.     UInt8    saveState;
  419.     UInt32    imageDescriptionSize;
  420.  
  421.  
  422.     imageAtom = QTFindChildByIndex( keySample, imagesContainerAtom, kSpriteImageAtomType, imageIndex, nil );
  423.     if ( imageAtom == 0 )    { err = cannotFindAtomErr; goto bail; }
  424.  
  425.     imageDataAtom = QTFindChildByIndex( keySample, imageAtom, kSpriteImageDataAtomType, 1, nil );
  426.     if ( imageDataAtom == 0 )    { err = cannotFindAtomErr; goto bail; }
  427.  
  428.     saveState = HGetState( (Handle)imageDesc );
  429.     HUnlock( (Handle)imageDesc );
  430.  
  431.     // Copy the data (ImageDescription followed by image data) to a handle
  432.  
  433.     FailOSErr( QTCopyAtomDataToHandle( keySample, imageDataAtom, (Handle)imageDesc ) );
  434.  
  435.     imageDescriptionSize = EndianU32_BtoN((**imageDesc).idSize);
  436.  
  437.     // Pull off anything following the image description (& its color table, if any, and
  438.     //    any image description extensions.
  439.     SetHandleSize( (Handle)imageDesc, imageDescriptionSize );
  440.  
  441.  
  442.  
  443. #if TARGET_RT_LITTLE_ENDIAN
  444.     EndianUtils_ImageDescription_BtoN( imageDesc );
  445. #endif
  446.  
  447.     HSetState( (Handle)imageDesc, saveState );
  448.     err = MemError();
  449.     
  450. bail:
  451.     return err;
  452. }
  453.  
  454.  
  455.  
  456.  
  457. OSErr SetImageGroupID( QTAtomContainer keySample, QTAtom imagesContainerAtom, short imageIndex, long groupID )
  458. {
  459.     OSErr        err = noErr;
  460.     QTAtom        imageAtom, imageGroupAtom;
  461.     
  462.     imageAtom = QTFindChildByIndex( keySample, imagesContainerAtom, kSpriteImageAtomType, imageIndex, nil );
  463.     if ( imageAtom == 0 )    { err = cannotFindAtomErr; goto bail; }
  464.  
  465.     imageGroupAtom = QTFindChildByIndex( keySample, imageAtom, kSpriteImageGroupIDAtomType, 1, nil );
  466.     if ( imageGroupAtom == 0 ) {
  467.         FailOSErr( QTInsertChild( keySample, imageAtom, kSpriteImageGroupIDAtomType, 1, 1, 0, nil, &imageGroupAtom ) );
  468.     }
  469.  
  470.     groupID = EndianU32_NtoB(groupID);
  471.     FailOSErr( QTSetAtomData( keySample, imageGroupAtom, sizeof( groupID ), &groupID ) );
  472.  
  473. bail:
  474.     return err;
  475. }
  476.  
  477. OSErr GetImageGroupID( QTAtomContainer keySample, QTAtom imagesContainerAtom, short imageIndex, long *groupID )
  478. {
  479.     OSErr        err = noErr;
  480.     QTAtom        imageAtom, imageGroupAtom;
  481.  
  482.     imageAtom = QTFindChildByIndex( keySample, imagesContainerAtom, kSpriteImageAtomType, imageIndex, nil );
  483.     if ( imageAtom == 0 )    { err = cannotFindAtomErr; goto bail; }
  484.  
  485.     imageGroupAtom = QTFindChildByIndex( keySample, imageAtom, kSpriteImageGroupIDAtomType, 1, nil );
  486.     
  487.     if ( ! imageGroupAtom )
  488.         *groupID = 0;
  489.     else {
  490.         FailOSErr( QTCopyAtomDataToPtr( keySample, imageGroupAtom, false, sizeof(*groupID), (Ptr)groupID, nil ) );
  491.  
  492.         *groupID = EndianU32_BtoN(*groupID);        // return native endian long
  493.     }
  494.     
  495. bail:
  496.     return err;
  497. }
  498.  
  499.